import base64
import json
from pickle import dumps, loads

from django.http import HttpResponseBadRequest, JsonResponse
from django.shortcuts import render
from django.views import View
from django_redis import get_redis_connection
from utils.response_code import RETCODE

from apps.goods.models import SKU
from apps.verification import constants

# Create your views here.


class CartsView(View):
    """购物车数据的增删改查"""

    def get(self, request):
        """获取购物车数据"""
        user = request.user
        if user.is_authenticated:
            # 登录用户从redis中获取信息
            conn = get_redis_connection('carts')
            # 创建redis管道
            pipe = conn.pipeline()
            # 获取商品ID和数量
            pipe.hgetall('carts_%s' % user.id)
            # 获取已选中商品的ID
            pipe.smembers('selected_%s' % user.id)
            # 执行管道中的操作，保存返回值
            sku_id_count, selected_sku_ids = pipe.execute()
            # 获取数据，组织成与cookies数据统一的格式：sku_id{'count':xxx, 'selected': True}
            carts = {}
            for sku_id, count in sku_id_count.items():
                # 如果商品ID在已选中商品ID集合中，就将状态设置为True，否则为False
                if sku_id in selected_sku_ids:
                    selected = True
                else:
                    selected = False

                carts[int(sku_id)] = {
                    'count': int(count),
                    'selected': selected
                }
        else:
            # 未登录用户从cookies中获取数据
            cookie_str = request.COOKIES.get('carts')
            if cookie_str is None:
                carts = {}
            else:
                de_carts = base64.b64decode(cookie_str)
                carts = de_carts.loads(de_carts)
        # 根据商品ID查询出商品信息
        ids = carts.keys()
        skus = SKU.objects.filter(id__in=ids)

        skus_list = []
        # 购物车商品总数
        total_count = 0
        # 购物车商品总价
        total_amount = 0
        for sku in skus:
            skus_list.append({
                'id': sku.id,
                'name': sku.name,
                'count': carts.get(sku.id).get('count'),
                # 将True，转'True'，方便json解析
                'selected': str(carts.get(sku.id).get('selected')),
                'default_image_url': sku.default_image.url,
                'price': str(sku.price),  # 从Decimal('10.2')中取出'10.2'，方便json解析
                'amount': str(sku.price * carts.get(sku.id).get('count')),
            })
            total_count += carts[sku_id]['count']
            total_amount += carts.get(sku_id).get('count') * sku.price

        context = {
            'carts_skus': skus_list
        }
        return render(request, 'cart.html', context)

    def post(self, request):
        """新增购物车数据"""

        # 获取前端提交的数据
        data = json.loads(request.body.decode())
        sku_id = data.get('sku_id')
        count = data.get('count')

        # 验证数据
        if not all([sku_id, count]):
            return JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': '缺少参数'})

        # 检查商品是否存在
        try:
            SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return JsonResponse({'code': RETCODE.NODATAERR,
                                 'errmsg': '没有该商品信息'})

        # 验证商品数量是否正确
        try:
            count = int(count)
        except Exception:
            return JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': '商品数量错误'})

        # 根据用户登陆状态在不同的地方保存购物车信息
        if request.user.is_authenticated:
            # 用户已经登陆就保存在redis
            # 连接数据库
            conn = get_redis_connection('carts')
            pipe = conn.pipeline()
            # 将商品ID和数量保存在redis的hash结构中
            pipe.hset('carts_%s' % request.user.id, sku_id, count)
            # 将已选中的商品放在redis的另一个集合结构中
            pipe.sadd('selected_%s' % request.user.id, sku_id)
            pipe.execute()
            return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
        else:
            # 用户未登录就保存在cookie

            cookie_carts = request.COOKIES.get('carts')

            # 判断用户购物车中是否已有商品
            if cookie_carts:
                # 已有商品
                # 对数据进行base64解码
                de_carts = base64.b64decode(cookie_carts)
                # 将解码数据转换为字典
                carts_data = loads(de_carts)

                # 判断请求添加的商品是否在已在到购物车中
                if sku_id in carts_data:
                    # 已在购物车中，增加相应数量
                    origin_count = carts_data[sku_id][count]
                    count += origin_count

            # 没有商品，直接添加
            # 将数据组织成JSON格式
            carts = {
                sku_id: {
                    "count": count,
                    "selected": True
                }
            }

            # 将数据转换为bytes类型，准备进行下一步的编码
            data = dumps(carts)
            # 将数据进行base64编码
            en_data = base64.b64decode(data)

            response = JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
            # 编码完成后转换回字符串类型
            response.set_cookie('carts', en_data.decode())
            return response

    def put(self, request):
        """更新购物车数据"""
        # 接收数据
        data = json.loads(request.body.decode())
        sku_id = data.get('sku_id')
        count = data.get('count')
        selected = data.get('selected', False)
        # 验证数据
        if not all([sku_id, count]):
            return JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': '缺少参数'})

        # 检查商品是否存在
        try:
            sku = SKU.objects.get(id=sku_id)
        except SKU.DoesNotExist:
            return JsonResponse({'code': RETCODE.NODATAERR,
                                 'errmsg': '没有该商品信息'})

        # 验证商品数量是否正确
        try:
            count = int(count)
        except Exception:
            return JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': '商品数量错误'})

        # 判断用户登陆状态
        if request.user.is_authenticated:
            # 连接redis
            conn = get_redis_connection('carts')
            pipe = conn.pipeline()
            # 保存商品ID和数量
            pipe.hset('carts_%s' % request.user.id, sku_id, count)
            # 保存已选中商品的ID，移除选中状态为false的
            if selected:
                pipe.sadd('carts_%s' % request.user.id, sku_id)
            else:
                pipe.srem('selected_%s' % request.user.id, sku_id)
            pipe.execute()

            # 创建响应对象
            cart_sku = {
                'id': sku_id,
                'count': count,
                'selected': selected,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price,
                'amount': sku.price * count,
            }
            return JsonResponse({'code': RETCODE.OK,
                                 'errmsg': 'OK',
                                 'cart_sku': cart_sku})
        else:
            cookies_str = request.COOKIES.get('carts')
            if cookies_str is None:
                carts = {}
            else:
                carts = loads(base64.b64decode(cookies_str))
                if sku_id in carts:
                    carts[sku_id] = {
                        'count': count,
                        'selected': selected
                    }
            # 对数据进行编码
            en_carts = base64.b64encode(dumps(carts))

            # 创建响应对象
            cart_sku = {
                'id': sku_id,
                'count': count,
                'selected': selected,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price,
                'amount': sku.price * count,
            }

            response = JsonResponse({'code': RETCODE.OK,
                                     'errmsg': 'OK',
                                     'cart_sku': cart_sku})
            response.set_cookie('carts', en_carts)
            return response

    def delete(self, request):
        """删除购物车数据"""
        # 接收数据
        data = json.loads(request.body.decode())
        sku_id = data.get('sku_id')
        count = data.get('count')
        # 判断用户登陆状态
        if request.user.is_authenticated:
            # 登录用户，删除redis中的数据
            conn = get_redis_connection('carts')
            pipe = conn.pipeline()
            pipe.hdel('carts_%s' % request.user.id, sku_id, count)
            pipe.srem('selected_%s' % request.user.id, sku_id)
            pipe.execute()
            return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})

        else:
            # 未登录用户，删除cookies中的数据
            cookies_str = request.COOKIES.get('carts')
            if cookies_str is None:
                carts = {}
            else:
                carts = loads(base64.b64decode(cookies_str))
                if sku_id in carts:
                    del carts[sku_id]
            response = JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK'})
            en_cookies = base64.b64encode(dumps(carts))
            response.set_cookie('caets', en_cookies.decode())
            return response


class CartsSelectAllView(View):
    """购物车全选"""

    def put(self, request):
        # 接收参数
        json_dict = json.loads(request.body.decode())
        selected = json_dict.get('selected', True)  # 为True表示全选，False表示取消全选
        # 校验参数
        if selected:
            # isinstance判断selected是不是布尔类型或其子类
            if not isinstance(selected, bool):
                return HttpResponseBadRequest('参数selected有误')

        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 用户已登录，操作redis购物车
            redis_conn = get_redis_connection('carts')
            cart = redis_conn.hgetall('carts_%s' % user.id)
            sku_id_list = cart.keys()
            if selected:
                # 全选
                redis_conn.sadd('selected_%s' % user.id, *sku_id_list)  # *表示拆包
            else:
                # 取消全选
                redis_conn.srem('selected_%s' % user.id, *sku_id_list)
        else:
            # 用户未登录，操作cookie购物车
            cart = request.COOKIES.get('carts')
            response = JsonResponse({'code': RETCODE.OK, 'errmsg': '全选购物车成功'})
            if cart is not None:
                cart = loads(base64.b64decode(cart.encode()))
                for sku_id in cart:
                    cart[sku_id]['selected'] = selected

                cookie_cart = base64.b64encode(dumps(cart)).decode()
                response.set_cookie('carts', cookie_cart,
                                    max_age=constants.CARTS_COOKIE_EXPIRES)

            return response


class CartsSimpleView(View):
    """右上角购物车简单展示"""

    def get(self, request):
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 用户已登录，查询Redis购物车
            redis_conn = get_redis_connection('carts')
            pipe = redis_conn.pipeline()
            # 获取购物车中的所有商品
            redis_cart = pipe.hgetall('carts_%s' % user.id)
            # 获取选中状态的所有商品
            cart_selected = pipe.smembers('selected_%s' % user.id)
            pipe.execute()

            # 将redis中的两个数据统一格式，跟cookie中的格式一致，方便统一查询
            cart_dict = {}
            for sku_id, count in redis_cart.items():
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    # sku_id在已选中商品的ID集合中，则为True，否则为False
                    'selected': sku_id in cart_selected
                }
        else:
            # 用户未登录，查询cookie购物车
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                # cookies中carts不为空，进行解码
                cart_dict = loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}

        # 构造简单购物车JSON数据
        cart_skus = []
        sku_ids = cart_dict.keys()
        # 从数据库中过滤出购物车中的商品
        skus = SKU.objects.filter(id__in=sku_ids)
        for sku in skus:
            cart_skus.append({
                'id': sku.id,
                'name': sku.name,
                'count': cart_dict.get(sku.id).get('count'),
                'default_image_url': sku.default_image.url
            })

        # 响应json列表数据
        return JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK',
                             'cart_skus': cart_skus})
